//+------------------------------------------------------------------+
//|                                                         6666.mq5 |
//|                                  Copyright 2025, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."  // 版权声明
#property link      "https://www.mql5.com"             // 官方链接
#property version   "1.00"                             // 版本号

// 输入参数 - 可在EA设置界面配置的参数
input ulong      InpMagicNumber = 123456;     // EA魔术编号（区分不同EA的唯一标识）
input string     InpNoTradeTimeStart = "19:30"; // 不交易时间段开始（服务器时间，格式HH:MM）
input string     InpNoTradeTimeEnd = "23:30";   // 不交易时间段结束（服务器时间，格式HH:MM）
input double     InpProfitMultiplier = 2.0;   // 盈利乘数（用于计算平仓目标价格）
input int        InpMAPeriod1 = 5;            // 第一条均线周期（用于技术分析）
input int        InpMAPeriod2 = 10;           // 第二条均线周期
input int        InpMAPeriod3 = 15;           // 第三条均线周期
input int        InpMAPeriod4 = 20;           // 第四条均线周期
input int        InpMAPeriod5 = 25;           // 第五条均线周期
input int        InpMAPeriod6 = 30;           // 第六条均线周期
input ENUM_APPLIED_PRICE InpMAAppliedPrice = PRICE_CLOSE; // 均线计算使用的价格（此处用收盘价）
input ENUM_MA_METHOD InpMAMethod = MODE_SMA;  // 均线计算方法（简单移动平均）
input int        InpGridStep = 30;           // 网格交易间距（以点为单位，决定加仓间隔）
input double     InpLotSize1 = 0.01;          // 第一次加仓手数
input double     InpLotSize2 = 0.01;          // 第二次加仓手数
// 后续加仓手数按顺序定义，支持最多15次加仓
input double     InpLotSize3 = 0.02;          // 第三次加仓手数（通常递增）
input double     InpLotSize4 = 0.04;          // 第四次加仓手数
input double     InpLotSize5 = 0.08;          // 第五次加仓手数
input double     InpLotSize6 = 0.52;          // 第六次加仓手数
input double     InpLotSize7 = 0.52;          // 第七次加仓手数
input double     InpLotSize8 = 1.04;          // 第八次加仓手数
input double     InpLotSize9 = 2.08;          // 第九次加仓手数
input double     InpLotSize10 = 4.16;         // 第十次加仓手数
input double     InpLotSize11 = 16.29;         // 第十一次加仓手数
input double     InpLotSize12 = 16.29;         // 第十二次加仓手数
input double     InpLotSize13 = 32.58;         // 第十三次加仓手数
input double     InpLotSize14 = 65.16;         // 第十四次加仓手数
input double     InpLotSize15 = 130.12;        // 第十五次加仓手数

// 全局变量 - 跨函数共享的数据
int maHandles[6];        // 存储6条均线指标的句柄（用于后续指标数据获取）
double maValues[6];      // 存储6条均线的当前值
datetime lastBarTime = 0;// 记录最后处理的K线时间（用于时间过滤）
bool filterEnabled = false; // 持仓过滤开关（控制是否启用持仓检查逻辑）
long chartID = 0;       // 当前图表ID（用于多图表环境区分）
int buttonId = 1;       // 按钮ID（预留UI交互使用）
int flage = -1;         // 多空方向标志（1=多头，0=空头，-1=未确定）
double lastOrderPrice = 0; // 最后一次开仓的价格（用于计算加仓间隔）
int orderCount = 0;     // 已执行的订单数量（记录加仓次数）
double lotSizes[15];    // 加仓手数数组（存储所有预设的加仓手数）

//+------------------------------------------------------------------+
//| EA初始化函数（启动时执行一次）                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   // 初始化加仓手数数组：将输入参数按顺序存入数组，便于统一管理
   lotSizes[0] = InpLotSize1;
   lotSizes[1] = InpLotSize2;
   lotSizes[2] = InpLotSize3;
   lotSizes[3] = InpLotSize4;
   lotSizes[4] = InpLotSize5;
   lotSizes[5] = InpLotSize6;
   lotSizes[6] = InpLotSize7;
   lotSizes[7] = InpLotSize8;
   lotSizes[8] = InpLotSize9;
   lotSizes[9] = InpLotSize10;
   lotSizes[10] = InpLotSize11;
   lotSizes[11] = InpLotSize12;
   lotSizes[12] = InpLotSize13;
   lotSizes[13] = InpLotSize14;
   lotSizes[14] = InpLotSize15;
   
   // 打印初始化信息：显示魔术编号，用于确认EA实例
   Print("EA初始化 - 魔术编号: ", InpMagicNumber);
   return(INIT_SUCCEEDED); // 返回初始化成功状态
}

//+------------------------------------------------------------------+
//| EA反初始化函数（卸载时执行）                                     |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   // 打印卸载信息：记录魔术编号，便于日志追踪
   Print("EA已卸载，魔术编号: ", InpMagicNumber);
}

//+------------------------------------------------------------------+
//| 行情更新处理函数（每个报价更新时触发）                            |
//+------------------------------------------------------------------+
void OnTick()
{
   flage=1; // 初始化方向标志为多头（实际逻辑可能根据策略调整）
   // 调用核心交易逻辑函数：马丁格尔网格交易策略
   MartinGridTrading();
}

//+------------------------------------------------------------------+
//| 持仓状态检查与恢复函数                                           |
//+------------------------------------------------------------------+
void CheckAndRestoreState()
{
   int total = PositionsTotal(); // 获取当前持仓总数
   if(total == 0) 
   {
      // 无持仓时跳过恢复流程
      Print("没有持仓，退出恢复状态检查");
      return;
   }
   
   // 初始化持仓统计变量
   int buyPositions = 0;       // 多头持仓数量
   int sellPositions = 0;      // 空头持仓数量
   double buyVolume = 0;       // 多头总手数
   double sellVolume = 0;      // 空头总手数
   double lowestBuyPrice = DBL_MAX; // 最低多头开仓价（用于确定平均成本）
   double highestSellPrice = 0; // 最高空头开仓价（用于确定平均成本）
   
   // 遍历所有持仓
   for(int i = 0; i < total; i++)
   {
      ulong ticket = PositionGetTicket(i); // 获取持仓订单号
      if(PositionSelectByTicket(ticket))   // 选择当前持仓
      {
         string symbol = PositionGetString(POSITION_SYMBOL); // 获取品种名称
         if(symbol == _Symbol) // 仅处理当前图表品种
         {
            long type = PositionGetInteger(POSITION_TYPE); // 获取持仓类型（多/空）
            double volume = PositionGetDouble(POSITION_VOLUME); // 获取持仓手数
            double price = PositionGetDouble(POSITION_PRICE_OPEN); // 获取开仓价格
            
            if(type == POSITION_TYPE_BUY)
            {
               // 统计多头持仓
               buyPositions++;
               buyVolume += volume;
               // 更新最低多头开仓价（用于后续加仓判断）
               if(price < lowestBuyPrice) lowestBuyPrice = price;
            }
            else if(type == POSITION_TYPE_SELL)
            {
               // 统计空头持仓
               sellPositions++;
               sellVolume += volume;
               // 更新最高空头开仓价（用于后续加仓判断）
               if(price > highestSellPrice) highestSellPrice = price;
            }
         }
      }
   }
   
   // 确定主要持仓方向
   if(buyPositions > 0 && sellPositions > 0)
   {
      // 同时存在多空持仓时视为异常状态
      Print("发现双向持仓，无法恢复状态");
      return;
   }
   else if(buyPositions > 0)
   {
      // 恢复多头状态：设置方向标志、记录最后开仓价和订单计数
      flage = 1;
      lastOrderPrice = lowestBuyPrice; // 以最低开仓价作为当前参考价格
      orderCount = EstimateOrderCount(buyVolume); // 根据总手数估算已执行订单数
      Print("恢复多头状态 - 最后开仓价: ", lastOrderPrice, " 订单计数: ", orderCount, " 总手数: ", buyVolume);
   }
   else if(sellPositions > 0)
   {
      // 恢复空头状态：设置方向标志、记录最后开仓价和订单计数
      flage = 0;
      lastOrderPrice = highestSellPrice; // 以最高开仓价作为当前参考价格
      orderCount = EstimateOrderCount(sellVolume); // 根据总手数估算已执行订单数
      Print("恢复空头状态 - 最后开仓价: ", lastOrderPrice, " 订单计数: ", orderCount, " 总手数: ", sellVolume);
   }
}

//+------------------------------------------------------------------+
//| 估算订单数量函数（根据总手数反推已执行的加仓次数）                |
//+------------------------------------------------------------------+
int EstimateOrderCount(double totalVolume)
{
   double cumulativeVolume = 0; // 累加手数变量
   for(int i = 0; i < ArraySize(lotSizes); i++)
   {
      cumulativeVolume += lotSizes[i]; // 累加预设的加仓手数
      // 当累加手数超过总持仓手数时（考虑浮点精度误差），返回当前订单数
      if(totalVolume <= cumulativeVolume + 0.0001) 
      {
         return i + 1; // 订单计数从1开始（初始仓位为第0次）
      }
   }
   return ArraySize(lotSizes); // 超过最大加仓次数时返回最大值
}

//+------------------------------------------------------------------+
//| 马丁网格交易核心逻辑函数                                         |
//+------------------------------------------------------------------+
void MartinGridTrading()
{
   // 首先检查并恢复持仓状态（处理EA重启后的状态同步）
   CheckAndRestoreState();
   
   // 方向未确定时跳过交易逻辑（初始状态或异常状态）
   if(flage == -1) return;  
   
   // 获取当前价格：多头用卖价（ASK），空头用买价（BID）
   double currentPrice = SymbolInfoDouble(_Symbol, flage == 1 ? SYMBOL_ASK : SYMBOL_BID);
   
   // 无持仓时开初始仓位
   if(PositionsTotal() == 0)
   {
      OpenInitialPosition(currentPrice); // 调用初始开仓函数
      return;
   }
   
   // 有持仓时检查是否需要加仓
   CheckForAddingPosition(currentPrice); // 调用加仓检查函数
}

//+------------------------------------------------------------------+
//| 开初始仓位函数                                                   |
//+------------------------------------------------------------------+
void OpenInitialPosition(double price)
{
   // 重置订单计数和参考价格（初始仓位视为第0次加仓）
   orderCount = 0;
   lastOrderPrice = price;
   
   // 使用第一组预设手数作为初始仓位
   double lotSize = lotSizes[0];
   
   // 构建交易请求结构
   MqlTradeRequest request = {};
   MqlTradeResult result = {};
   
   request.action = TRADE_ACTION_DEAL;       // 即时成交类型
   request.symbol = _Symbol;                 // 当前品种
   request.volume = lotSize;                 // 开仓手数
   request.type = (flage == 1) ? ORDER_TYPE_BUY : ORDER_TYPE_SELL; // 多空方向
   request.price = price;                    // 开仓价格
   request.deviation = 10;                   // 最大滑点（10点）
   request.magic = InpMagicNumber;           // 魔术编号（标识EA订单）
   request.comment = "Initial position";     // 订单注释
   
   // 发送交易请求
   if(!OrderSend(request, result))
   {
      // 处理开仓失败情况
      Print("开初始仓位失败, 错误码: ", GetLastError());
      return;
   }
   
   // 记录开仓成功信息
   Print("初始仓位已开: ", EnumToString(request.type), " 价格: ", price, " 手数: ", lotSize);
   orderCount++; // 订单计数+1（初始仓位计为第1次）
}

//+------------------------------------------------------------------+
//| 加仓条件检查函数                                                 |
//+------------------------------------------------------------------+
void CheckForAddingPosition(double currentPrice)
{
   // 计算当前价格与最后开仓价的差值（以点为单位，方向根据多空调整）
   // 多头：当前价格下跌时亏损，差值为负；空头：当前价格上涨时亏损，差值为正
   int priceDiff = (int)((currentPrice - lastOrderPrice) / _Point * (flage == 1 ? -1 : 1));
   
   // 当亏损达到或超过网格间距时触发加仓
   if(priceDiff >= InpGridStep)
   {
      // 计算需要执行的加仓次数（按网格间距整除）
      int steps = priceDiff / InpGridStep;
      
      // 限制加仓次数不超过预设手数数组大小
      if(orderCount + steps > ArraySize(lotSizes))
      {
         steps = ArraySize(lotSizes) - orderCount; // 取剩余可加次数
         if(steps <= 0) return; // 无剩余次数时返回
      }
      
      // 批量执行多次加仓（处理连续达到加仓条件的情况）
      for(int i = 0; i < steps; i++)
      {
         AddPosition(currentPrice); // 调用具体加仓函数
      }
   }
}

//+------------------------------------------------------------------+
//| 执行加仓函数                                                     |
//+------------------------------------------------------------------+
void AddPosition(double price)
{
   // 防止超过最大加仓次数
   if(orderCount >= ArraySize(lotSizes)) return;
   
   // 获取当前加仓手数（根据订单计数索引）
   double lotSize = lotSizes[orderCount];
   
   // 构建交易请求（与初始开仓逻辑类似，手数和注释不同）
   MqlTradeRequest request = {};
   MqlTradeResult result = {};
   
   request.action = TRADE_ACTION_DEAL;
   request.symbol = _Symbol;
   request.volume = lotSize;
   request.type = (flage == 1) ? ORDER_TYPE_BUY : ORDER_TYPE_SELL; // 保持与初始方向一致
   request.price = price;
   request.deviation = 10;
   request.magic = InpMagicNumber;
   request.comment = "Add position #" + IntegerToString(orderCount + 1); // 标注加仓次数
   
   // 发送交易请求
   if(!OrderSend(request, result))
   {
      // 处理加仓失败情况
      Print("加仓失败, 错误码: ", GetLastError());
      return;
   }
   
   // 记录加仓成功信息
   Print("加仓 #", orderCount + 1, ": ", EnumToString(request.type), " 价格: ", price, " 手数: ", lotSize);
   lastOrderPrice = price; // 更新最后开仓价为当前加仓价格
   orderCount++; // 订单计数递增
}